﻿
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
//int main()
//{
//
//	// sizeof计算变量所占内存空间的大小,单位是字节
//	// 如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小
//	// 只关注占用内存空间的大小,不在乎内存中存放什么数据
//
//	int a = 10;
//	printf("%zd\n",sizeof(a)); // 4
//	printf("%zd\n",sizeof(a+2));  // 4
//	printf("%zd\n",sizeof(int)); // 4
//	return 0;
//}

//int main()
//{
//	// strlen()函数统计\0之前字符串中字符的个数
//	char arr1[3] = { 'a','b','c' };
//	char arr2[] = "abc"; // a b c \0
//	printf("%zd\n", strlen(arr1)); // 随机值
//	printf("%zd\n", strlen(arr2)); // 3
//
//	printf("%zd\n", sizeof(arr1)); // 3个字节
//	printf("%zd\n", sizeof(arr2)); // 4个字节
//	return 0;
//}

// 数组名的理解:
// 1.数组名是数组首元素的地址
// 但是有两个例外: 
// 1. sizeof(数组名),数组名单独放在sizeof内部,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
// 2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址

//int main()
//{
//	int a[] = { 1,2,3,4 };
//
//	printf("%zd\n", sizeof(a)); // a单独放在sizeof内部,表示整个数组,计算的就是整个数组的大小, 4*4 = 16
//	printf("%zd\n", sizeof(a+0)); // a是首元素的地址,a+0还是首元素的地址,计算地址的大小4/8
//	printf("%zd\n", sizeof(*a)); // a是首元素的地址,*a是首元素,计算首元素的大小:4个字节
//	printf("%zd\n", sizeof(a+1)); // a是首元素的地址,a+1是第二个元素的地址,是地址大小就是4/8
//	printf("%zd\n", sizeof(a[1])); // a[1]表示第二个元素,计算的是第二个元素的大小:4个字节
//	printf("%zd\n", sizeof(&a)); // &a - 取出的是数组的地址,是地址都是4/8个字节
//	// &a+1 跳过多少个字节:16
//	// *&a 这里的解引用访问到16个字节
//	// int (*)[4]
//	printf("%zd\n", sizeof(*&a));  //16
//	// *&a, * 和 &抵消了,sizeof(a) 
//	printf("%zd\n", sizeof(&a+1)); // &a+1是跳过整个数组,指向4的后面,其实也是地址,是地址就是4/8个字节
//	printf("%zd\n", sizeof(&a[0]));// &a[0]就是首元素的地址,4/8
//	printf("%zd\n", sizeof(&a[0]+1)); // &a[0]+1是第二个元素的地址, 4/8 
//
//	return 0;
//}

//int main()
//{
//	char arr[] = { 'a','b','c','d','e','f' };
//	printf("%zd\n", sizeof(arr)); // arr是数组名,单独放在sizeof内部,计算的是数组的大小 - 6
//	printf("%zd\n", sizeof(arr+0)); // arr是数组名,是首元素的地址,arr+0也是首元素的地址,是地址就是4/8
//	printf("%zd\n", sizeof(*arr));// arr是首元素的地址,*arr就是首元素,大小是1个字节
//	printf("%zd\n", sizeof(arr[1]));	// arr[1]是第二个元素,大小是1个字节
//	printf("%zd\n", sizeof(&arr));// &arr是数组的地址,数组的地址也是地址,大小是4/8
//	printf("%zd\n", sizeof(&arr + 1));// &arr是数组的地址,&arr+1就是跳过整个地址,指向f的后面, 是地址大小就是4/8
//	printf("%zd\n", sizeof(&arr[0] + 1)); // &arr[0]是数组首元素的地址,&arr[0]+1是第二个元素的地址,大小是4/8
//	return 0;
//}

//int main()
//{
//	char arr[] = { 'a','b','c','d','e','f' };
//	printf("%zd\n", strlen(arr)); // 随机值, arr是首元素的地址,数组中没有\0,一直往后找,什么时候遇到\0不清楚
//	printf("%zd\n", strlen(arr + 0)); // 随机值,arr是首元素的地址,arr+0依然是首元素的地址
//	printf("%zd\n", strlen(*arr)); // *arr是首元素, 是'a' - 97,传给strlen函数,97回被当做地址
//	// 以97作为地址,会形成非法访问,程序会崩溃
//	printf("%zd\n", strlen(arr[1])); // arr[1]是第二个元素,就是'b' - 98,传给strlen函数,98会被当做地址.同上,程序崩溃
//	printf("%zd\n", strlen(&arr)); // &arr是数组的地址,虽然是数组的地址,值和首元素的地址一样,strlen依然是从第一个字符的位置开始向后找\0,会得到随机值
//	printf("%zd\n", strlen(&arr + 1)); // &arr+1是f后面的地址,什么时候遇到\0,依然不知道,随机值
//	printf("%zd\n", strlen(&arr[0] + 1)); // &arr[0]+1就是第二个元素的地址,得到的也是随机值
//	return 0;
//}

//int main()
//{
//	char arr[] = "abcdef";
//	printf("%zd\n", sizeof(arr)); // 7 解析:arr表示整个数组,计算的是整个数组的大小,单位字节,7*1 = 7
//	printf("%zd\n", sizeof(arr + 0)); // 4/8 解析:arr就是首元素的地址,arr+0还是数组首元素的地址 4 / 8
//	printf("%zd\n", sizeof(*arr)); // 1 解析:arr就是数组首元素的地址,*arr是首元素,大小就是1个字节
//	printf("%zd\n", sizeof(arr[1])); // 1 解析:arr[1]数组的第二个元素,计算的就是第二个元素的大小,单位是字节 - 1
//	printf("%zd\n", sizeof(&arr)); // 4/8 解析:arr表示整个数组,&arr取出的是整个数组的地址,是地震大小就是4 / 8个字节
//	printf("%zd\n", sizeof(&arr + 1)); // 4/8 解析:&arr + 1是跳过这个数组后的地址,是地址大小就是4 / 8个字节
//	printf("%zd\n", sizeof(&arr[0] + 1)); // 4/8 解析:&arr[0]是数组首元素的地址,&arr[0]+1是数组第二个元素的地址: 4 / 8
//	return 0;
//}

//int main()
//{
//	char arr[] = "abcdef";
//	printf("%zd\n", strlen(arr)); // 6: arr是数组首元素的地址,从第一个元素开始,统计\0之前字符的个数
//	printf("%zd\n", strlen(arr + 0)); // 6: arr是数组首元素地址,arr+0还是数组首元素的地址,结果同上
//	//printf("%zd\n", strlen(*arr)); // 程序崩溃:arr是首元素的地址,*arr就是首元素,*arr == 'a' == 97,非法访问内存,导致程序崩溃
//	//printf("%zd\n", strlen(arr[1])); // 程序崩溃:arr[1]是第二个元素 == 'b' == 98,道理同上
//	printf("%zd\n", strlen(&arr)); // 6: &arr取出的是数组的地址,数组的地址和首元素的地址是同一个值,
//	// strlen也是从第一个字符开始向后统计\0之前的字符个数
//	printf("%zd\n", strlen(&arr + 1)); // 随机值:&arr+1跳过整个数组,指向\0的后面一个地址,这个地址后面的\0不知道在哪个位置,所以值也是随机的
//	printf("%zd\n", strlen(&arr[0] + 1)); // 5: &arr[0] + 1是第二个元素的地址,\0之前有5个元素
//	return 0;
//}

//int main()
//{
//	char* p = "abcdef";
//	printf("%zd\n", sizeof(p)); // 4 / 8 : p是一个指针变量,大小就是4/8个字节
//	printf("%zd\n", sizeof(p + 1)); // 4 / 8 : p中存放的是'a'的地址,p+1是'b'的地址,大小就是4/8个字节
//	printf("%zd\n", sizeof(*p)); // 1: *p == 'a'
//	printf("%zd\n", sizeof(p[0])); // 1: p[0] == *(p+0) == *p,结果就同上
//	printf("%zd\n", sizeof(&p)); // 4 / 8 : &p是指针变量p的地址,是地址大小就是4/8个字节
//	printf("%zd\n", sizeof(&p + 1)); // 4 / 8 : &p + 1还是地址大小是4/8个字节, &p+1是指向p变量的后边
//	printf("%zd\n", sizeof(&p[0] + 1)); // 4 / 8 : &p[0] + 1 是'b'的地址,大小是4/8个字节
//	return 0;
//}

//int main()
//{
//	char* p = "abcdef";
//	printf("%zd\n", strlen(p)); // 6： p里边存放是'a'的地址
//	printf("%zd\n", strlen(p + 1)); // 5: p+1是'b'的地址
//	//printf("%zd\n", strlen(*p)); // 非法访问,*p是'a' == 97,strlen会将97当成地址,程序崩溃
//	//printf("%zd\n", strlen(p[0])); // 非法访问,效果同上
//	printf("%zd\n", strlen(&p)); // 随机值,&p是p这个变量的地址,strlen就是从p这块空间的起始地址开始向后找\0的
//	// p中存放的地址是不明确的,所以有没有\0,什么时候会遇到\0都不确定
//	printf("%zd\n", strlen(&p + 1)); // 随机值:&p+1是p变量后边的地址,从这个位置向后的内存数据不知道
//	// 什么时候会遇到\0都不确定
//	printf("%zd\n", strlen(&p[0] + 1)); // 5
//
//	return 0;
//}

//int main()
//{
//	int a[3][4] = { 0 };
//	printf("%zd\n", sizeof(a)); //48: a是数组名,单独放在sizeof内部,表示整个数组,计算的是整个数组的大小,单位是字节3*4*4 = 48
//	printf("%zd\n", sizeof(a[0][0])); //4: a[0][0]是第一行第一个元素
//	printf("%zd\n", sizeof(a[0])); //16: a[0]是第一行这个一维数组的数组名,数组名单独放在sizeof内部,计算的是第一行这个一维数组的大小
//	printf("%zd\n", sizeof(a[0] + 1)); // 4/8
//	// a[0]是数组名,这里表示数组首元素的地址,是第一行第一个元素的地址
//	// a[0]+1就是第一行第二个元素的地址,是地址大小就是4/8
//	printf("%zd\n", sizeof(*(a[0] + 1))); // 4:*(a[0]+1)是第一行第二个元素-大小是4个字节
//	printf("%zd\n", sizeof(a + 1)); // 4 / 8: a+1就是第二行的地址,是地址就是4/8个字节
//	// a是二维数组的数组名,在这里表示首元素的地址,也就是第一行的地址
//	// a+1就是第二行的地址
//	printf("%zd\n", sizeof(*(a + 1))); // 16
//	//a+1是第二行的地址,*(a+1)得到的就是第二行
//	//int(*)[4]		  对数组指针解引用,放一个数组,就是一行的一维数组
//	//*(a+1) == a[1],a[1]是第二行的数组名,sizeof(arr[1])计算的是第二行的大小
//	printf("%zd\n", sizeof(&a[0] + 1)); // 4 / 8
//	//a[0]是第一行的数组名,&a[0]取出的是第一行这个一维数组的地址
//	//&a[0]+1就是第二行的地址
//	printf("%zd\n", sizeof(*(&a[0] + 1))); // 16 *(&a[0]+1)是第二行,计算的是第二行的大小,16个字节
//	printf("%zd\n", sizeof(*a)); // 16: a是二维数组的数组名,a是首元素的地址,就是第一行的地址,*a就是第一行
//	//计算的是第一行的大小,16字节
//	//*a == *(a+0) == a[0]
//	printf("%zd\n", sizeof(a[3])); // 16:没有越界访问,sizeof内部的表达式是不计算的
//	// sizeof(int(*)[4])
//	// sizeof(int) - 4
//	// sizeof(4+3); --4
//
//	return 0;
//}
 
// 画图-->数组,指针运算题
//int main()
//{
//	int a[5] = { 1, 2, 3, 4, 5 };
//	int* ptr = (int*)(&a + 1);
//	printf("%d,%d", *(a + 1), *(ptr - 1)); // 2,5
//	return 0;
//}

//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥？
//考点:指针+-整数运算
//
//指针+1到底跳过多少个字节,取决于指针的类型,其实就是指针指向了什么类型的数据
//struct Test
//{
//	int Num;
//	char* pcName;
//	short sDate;
//	char cha[2];
//	short sBa[4];
//}*p = (struct Test*)0x100000;
//
//int main()
//{
//	printf("%p\n", p + 0x1); // 0x100014 :跳过一个结构体数据,一个结构体是20个字节  0x100020 //err
//	printf("%p\n", (unsigned long)p + 0x1); // 0x10001 //这里是不是指针+1? 不是.其实是整数+1
//	// 0x100000+1 -->0x100001
//	printf("%p\n", (unsigned int*)p + 0x1); // 0x100004
//	return 0;
//}

//int main()
//{
//	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//	// 逗号表达式:    1 , 3 ,  5, 0,  0 , 0
//	int* p;
//	p = a[0];
//	printf("%d", p[0]);// 1
//	return 0;
//}

//假设环境是x86环境,程序输出的结果是啥？


//int main()
//{
//	int a[5][5];
//	int(*p)[4];
//	p = a;
//	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); // FFFFFFFC,-4
//
//	// 1. 指针-指针的前提是两个指针指向同一块空间
//	// 2. 指针-指针的的结果的绝对值是两个指针之间的元素个数
//	// 小地址-大地址 会得到负数
//	// %p - 打印地址的,直接将内存中的值,当作地址打印
//	return 0;
//}


//int main()
//{
//	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	int* ptr1 = (int*)(&aa + 1);
//	int* ptr2 = (int*)(*(aa + 1));
//	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); // ptr1[-1],ptr2[-1]
//
//	return 0;
//}


//int main()
//{
//	char* a[] = { "work","at","alibaba" };
//	char** pa = a;
//	pa++;
//	printf("%s\n", *pa); //at
//	return 0;
//}

// 画图解决

int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp); // point
	printf("%s\n", *-- * ++cpp + 3); //ER
	printf("%s\n", *cpp[-2] + 3); // ST
	printf("%s\n", cpp[-1][-1] + 1); // EW
	return 0;
}